﻿using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using RDF;

using stepengine = RDF.ifcengine;

#if _WIN64
using int_t = System.Int64;
#else
using int_t = System.Int32;
#endif

namespace DZJIFC.Model
{
    public class IFCModel : Model
    {
        #region Constants

        public static int_t DEFAULT_CIRCLE_SEGMENTS = 36;

        #endregion // Constants

        #region Fields

        // Entities
        int_t _ifcProjectEntity;
        int_t _ifcSpaceEntity;
        int_t _ifcOpeningElementEntity;
        int_t _ifcDistributionElementEntity;
        int_t _ifcElectricalElementEntity;
        int_t _ifcElementAssemblyEntity;
        int_t _ifcElementComponentEntity;
        int_t _ifcEquipmentElementEntity;
        int_t _ifcFeatureElementEntity;
        int_t _ifcFeatureElementSubtractionEntity;
        int_t _ifcFurnishingElementEntity;
        int_t _ifcReinforcingElementEntity;
        int_t _ifcTransportElementEntity;
        int_t _ifcVirtualElementEntity;

        #endregion // Fields

        #region Methods

        public IFCModel()
            : base(ModelType.IFC)
        {
        }

        protected override bool LoadCore(string strFilePath)
        {
            if (!File.Exists(strFilePath))
            {
                return false;
            }

            Instance = stepengine.sdaiOpenModelBNUnicode(0, Encoding.Unicode.GetBytes(strFilePath), Encoding.Unicode.GetBytes(""));
            if (Instance == 0)
            {
                return false;
            }

            stepengine.GetSPFFHeaderItem(Instance, 9, 0, stepengine.sdaiUNICODE, out IntPtr outputValue);

            string strVersion = Marshal.PtrToStringUni(outputValue);
            if (!strVersion.Contains("IFC"))
            {
                return false;
            }

            stepengine.setBRepProperties(Instance, 7, 0.9, 0.0, 20000);

            _ifcSpaceEntity = stepengine.sdaiGetEntity(Instance, "IFCSPACE");
            _ifcOpeningElementEntity = stepengine.sdaiGetEntity(Instance, "IFCOPENINGELEMENT");
            _ifcDistributionElementEntity = stepengine.sdaiGetEntity(Instance, "IFCDISTRIBUTIONELEMENT");
            _ifcElectricalElementEntity = stepengine.sdaiGetEntity(Instance, "IFCELECTRICALELEMENT");
            _ifcElementAssemblyEntity = stepengine.sdaiGetEntity(Instance, "IFCELEMENTASSEMBLY");
            _ifcElementComponentEntity = stepengine.sdaiGetEntity(Instance, "IFCELEMENTCOMPONENT");
            _ifcEquipmentElementEntity = stepengine.sdaiGetEntity(Instance, "IFCEQUIPMENTELEMENT");
            _ifcFeatureElementEntity = stepengine.sdaiGetEntity(Instance, "IFCFEATUREELEMENT");
            _ifcFeatureElementSubtractionEntity = stepengine.sdaiGetEntity(Instance, "IFCFEATUREELEMENTSUBTRACTION");
            _ifcFurnishingElementEntity = stepengine.sdaiGetEntity(Instance, "IFCFURNISHINGELEMENT");
            _ifcReinforcingElementEntity = stepengine.sdaiGetEntity(Instance, "IFCREINFORCINGELEMENT");
            _ifcTransportElementEntity = stepengine.sdaiGetEntity(Instance, "IFCTRANSPORTELEMENT");
            _ifcVirtualElementEntity = stepengine.sdaiGetEntity(Instance, "IFCVIRTUALELEMENT");

            RetrieveObjectsRecursively(stepengine.sdaiGetEntity(Instance, "IFCOBJECT"), DEFAULT_CIRCLE_SEGMENTS);
            RetrieveObjects("IFCPROJECT", DEFAULT_CIRCLE_SEGMENTS);
            RetrieveObjects("IFCRELSPACEBOUNDARY", DEFAULT_CIRCLE_SEGMENTS);

            Scale();

            FireModelLoaded();

            return true;
        }

        private void RetrieveObjects(string strEntityName, int_t iCircleSegments)
        {
            int_t iInstances = stepengine.sdaiGetEntityExtentBN(Instance, strEntityName);
            int_t iInstancesCount = stepengine.sdaiGetMemberCount(iInstances);

            for (int_t i = 0; i < iInstancesCount; ++i)
            {
                int_t iInstance = 0;
                stepengine.engiGetAggrElement(iInstances, i, stepengine.sdaiINSTANCE, out iInstance);
                Debug.Assert(iInstance != 0);

                PreLoadInstance(iInstance);

                if (iCircleSegments != DEFAULT_CIRCLE_SEGMENTS)
                {
                    stepengine.circleSegments(iCircleSegments, 5);
                }

                IFCInstance ifcInstance = new IFCInstance(_iInstanceID++, iInstance, strEntityName);

                if (iCircleSegments != DEFAULT_CIRCLE_SEGMENTS)
                {
                    stepengine.circleSegments(DEFAULT_CIRCLE_SEGMENTS, 5);
                }

                ifcInstance.Enabled =
                    strEntityName.ToUpper() == "IFCSPACE" ||
                    strEntityName.ToUpper() == "IFCRELSPACEBOUNDARY" ||
                    strEntityName.ToUpper() == "IFCOPENINGELEMENT" ||
                    strEntityName.ToUpper() == "IFCALIGNMENTVERTICAL" ||
                    strEntityName.ToUpper() == "IFCALIGNMENTHORIZONTAL" ||
                    strEntityName.ToUpper() == "IFCALIGNMENTSEGMENT" ||
                    strEntityName.ToUpper() == "IFCALIGNMENTCANT" ? false : true;

                if (!IFCInstances.ContainsKey(ifcInstance.Instance))
                {
                    IFCInstances.Add(ifcInstance.Instance, ifcInstance);
                    ID2IFCInstance[ifcInstance.ID] = ifcInstance;
                }

                if (!Geometries.ContainsKey(ifcInstance.Instance))
                {
                    ifcInstance.Instances.Add(ifcInstance); // Geometry

                    Geometries[ifcInstance.Instance] = ifcInstance;
                    Instances[ifcInstance.ID] = ifcInstance;
                }

                stepengine.cleanMemory(Instance, 0);
            }
        }

        private void RetrieveObjectsRecursively(int_t iParentEntity, int_t iCircleSegments)
        {
            if (iParentEntity == _ifcDistributionElementEntity ||
                iParentEntity == _ifcElectricalElementEntity ||
                iParentEntity == _ifcElementAssemblyEntity ||
                iParentEntity == _ifcElementComponentEntity ||
                iParentEntity == _ifcEquipmentElementEntity ||
                iParentEntity == _ifcFeatureElementEntity ||
                iParentEntity == _ifcFurnishingElementEntity ||
                iParentEntity == _ifcTransportElementEntity ||
                iParentEntity == _ifcVirtualElementEntity)
            {
                iCircleSegments = 12;
            }

            if (iParentEntity == _ifcReinforcingElementEntity)
            {
                iCircleSegments = 6;
            }

            int_t iInstances = stepengine.sdaiGetEntityExtent(Instance, iParentEntity);
            int_t iIntancesCount = stepengine.sdaiGetMemberCount(iInstances);

            if (iIntancesCount != 0)
            {
                stepengine.engiGetEntityName(iParentEntity, stepengine.sdaiUNICODE, out IntPtr name);

                string strParentEntityName = Marshal.PtrToStringUni(name);

                RetrieveObjects(strParentEntityName, iCircleSegments);
            }

            iIntancesCount = stepengine.engiGetEntityCount(Instance);
            for (int_t i = 0; i < iIntancesCount; i++)
            {
                int_t iEntity = stepengine.engiGetEntityElement(Instance, i);
                if (stepengine.engiGetEntityParent(iEntity) == iParentEntity)
                {
                    RetrieveObjectsRecursively(iEntity, iCircleSegments);
                }
            }
        }

        protected override void Reset()
        {
            base.Reset();

            IFCInstances = new Dictionary<int_t, IFCInstance>();
            ID2IFCInstance = new Dictionary<int_t, IFCInstance>();
        }

        #endregion // Methods

        #region Properties

        // SdaiInstance : IFCInstance
        public Dictionary<int_t, IFCInstance> IFCInstances { get; private set; } = new Dictionary<int_t, IFCInstance>(); //#todo - use Instances?
        // ID : IFCInstance
        public Dictionary<int_t, IFCInstance> ID2IFCInstance { get; private set; } = new Dictionary<int_t, IFCInstance>(); //#todo - use Instances?

        #endregion // Properties
    }
}
